<!DOCTYPE html>



  


<html class="theme-next muse use-motion" lang="zh-CN">
<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform">
<meta http-equiv="Cache-Control" content="no-siteapp">
















  
  
  <link href="/blog/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css">







<link href="/blog/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css">

<link href="/blog/css/main.css?v=5.1.4" rel="stylesheet" type="text/css">


  <link rel="apple-touch-icon" sizes="180x180" href="/blog/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/blog/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/blog/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/blog/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="Hexo, NexT">










<meta name="description" content="Java源码/设计模式/个人记录">
<meta property="og:type" content="website">
<meta property="og:title" content="arry&#39;s blog">
<meta property="og:url" content="https://arrycoder.gitee.io/page/2/index.html">
<meta property="og:site_name" content="arry&#39;s blog">
<meta property="og:description" content="Java源码/设计模式/个人记录">
<meta property="og:locale" content="zh-CN">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="arry&#39;s blog">
<meta name="twitter:description" content="Java源码/设计模式/个人记录">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/blog/',
    scheme: 'Muse',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: 'Author'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://arrycoder.gitee.io/page/2/">





  <title>arry's blog</title>
  





  <script type="text/javascript">
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?97f9adfda8d04eb91dd054a98264d191";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>




</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-CN">

  
  
    
  

  <div class="container sidebar-position-left 
  page-home">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/blog/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">arry's blog</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle"></p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/blog/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br>
            
            Home
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/blog/tags/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br>
            
            Tags
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/blog/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br>
            
            Categories
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/blog/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br>
            
            Archives
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            
  <section id="posts" class="posts-expand">
    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://arrycoder.gitee.io/blog/并发/线程封闭/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="arry">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/blog/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="arry's blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/并发/线程封闭/" itemprop="url">线程封闭</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Posted on</span>
              
              <time title="Post created" itemprop="dateCreated datePublished" datetime="2020-06-08T22:26:30+08:00">
                2020-06-08
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">In</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/并发/" itemprop="url" rel="index">
                    <span itemprop="name">并发</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h1 id="线程封闭"><a href="#线程封闭" class="headerlink" title="线程封闭"></a>线程封闭</h1><p>并发的实现是比较困难的，因此，直接避免产生多线程竞争也是一种保证线程安全的手段。</p>
<h2 id="局部变量"><a href="#局部变量" class="headerlink" title="局部变量"></a>局部变量</h2><p>局部变量就保证了一个变量，只能被一个线程使用，因此这个变量就肯定是线程安全的。</p>
<h2 id="ThreadLocal"><a href="#ThreadLocal" class="headerlink" title="ThreadLocal"></a>ThreadLocal</h2><p><strong>ThreadLocal</strong> 是Java提供的一个线程内部存储的工具，在代码里我们随时可以从threadLocal里存取数据，因为ThreadLocal也是线程之间互相隔离的，因此也不会有并发问题发生。</p>
<h3 id="源码"><a href="#源码" class="headerlink" title="源码"></a>源码</h3><p>首先在源码结构上解释一下，为什么我们通过ThreadLocal获取对象的时候，都是当前线程独占的。</p>
<p>ThreadLocal内部提供了一个数据结构<strong>ThreadLocalMap</strong>，在<strong>每个线程中</strong>会维护一个<strong>ThreadLocalMap</strong>。注意，是<strong>每个线程</strong>都会维护一个。因此，我们在使用ThreadLocal的时候，其实都是通过ThreadLocal来操作每个线程上独占的一个对象。</p>
<p>Thread类中持有的threadLocals，在Thread类中仅仅是声明了这个对象。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* ThreadLocal values pertaining to this thread. This map is maintained</span></span><br><span class="line"><span class="comment"> * by the ThreadLocal class. */</span></span><br><span class="line">ThreadLocal.ThreadLocalMap threadLocals = <span class="keyword">null</span>;</span><br></pre></td></tr></table></figure>

<p>首先，从最常用到的ThreadLocal对外提供的几个方法入手</p>
<h3 id><a href="#" class="headerlink" title></a><img src="/blog/并发/线程封闭/ThreadLocal.png" alt="image-20200610165411346"></h3><h4 id="void-set-T"><a href="#void-set-T" class="headerlink" title="void set(T)"></a>void set(T)</h4><p>从当前线程里获取threadLocalMap，如果为null则new一个ThreadLocalMap。这个ThreadLocalMap后面会再介绍，现在可以先将他当作一个普通的Map。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">set</span><span class="params">(T value)</span> </span>&#123;</span><br><span class="line"> 		<span class="comment">// 获取当前线程</span></span><br><span class="line">    Thread t = Thread.currentThread();</span><br><span class="line">    <span class="comment">// 从当前线程里得到threadLocalMap</span></span><br><span class="line">    ThreadLocalMap map = getMap(t);</span><br><span class="line">    <span class="keyword">if</span> (map != <span class="keyword">null</span>)</span><br><span class="line">        map.set(<span class="keyword">this</span>, value);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        createMap(t, value);</span><br><span class="line">&#125;</span><br><span class="line">- getMap</span><br><span class="line"><span class="function">ThreadLocalMap <span class="title">getMap</span><span class="params">(Thread t)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> t.threadLocals;</span><br><span class="line">	&#125;</span><br><span class="line">- createMap</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">createMap</span><span class="params">(Thread t, T firstValue)</span> </span>&#123;</span><br><span class="line">        t.threadLocals = <span class="keyword">new</span> ThreadLocalMap(<span class="keyword">this</span>, firstValue);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="T-get"><a href="#T-get" class="headerlink" title="T get()"></a>T get()</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> T <span class="title">get</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    Thread t = Thread.currentThread();</span><br><span class="line">  <span class="comment">// 获取到当前线程的ThreadLocalMap</span></span><br><span class="line">    ThreadLocalMap map = getMap(t);</span><br><span class="line">  <span class="comment">// 如果map不为空 &amp;&amp; 获取到的value也不为空</span></span><br><span class="line">    <span class="keyword">if</span> (map != <span class="keyword">null</span>) &#123;</span><br><span class="line">        ThreadLocalMap.Entry e = map.getEntry(<span class="keyword">this</span>);</span><br><span class="line">        <span class="keyword">if</span> (e != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</span><br><span class="line">            T result = (T)e.value;</span><br><span class="line">            <span class="keyword">return</span> result;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> setInitialValue();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>setInitialValue是一个模板方法，通过继承 实现自己的initialValue方法，在未set过ThreadLocal的时候也能get到默认值。如果map为空就创建一个ThreadLocalMap放到线程里。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">  <span class="function"><span class="keyword">private</span> T <span class="title">setInitialValue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  <span class="comment">// ThreadLocal自带的方法里，initialValue返回的是null</span></span><br><span class="line">    T value = initialValue();</span><br><span class="line">    Thread t = Thread.currentThread();</span><br><span class="line">    ThreadLocalMap map = getMap(t);</span><br><span class="line">    <span class="keyword">if</span> (map != <span class="keyword">null</span>)</span><br><span class="line">      map.set(<span class="keyword">this</span>, value);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">      createMap(t, value);</span><br><span class="line">    <span class="keyword">return</span> value;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="withInitial"><a href="#withInitial" class="headerlink" title="withInitial"></a>withInitial</h4><p>很简单，用户传入一个Supplier方法，在get的时候会调用Supplier方法产生一个默认值。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> &lt;S&gt; <span class="function">ThreadLocal&lt;S&gt; <span class="title">withInitial</span><span class="params">(Supplier&lt;? extends S&gt; supplier)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> SuppliedThreadLocal&lt;&gt;(supplier);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">SuppliedThreadLocal</span>&lt;<span class="title">T</span>&gt; <span class="keyword">extends</span> <span class="title">ThreadLocal</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> Supplier&lt;? extends T&gt; supplier;</span><br><span class="line">				<span class="comment">// 构造方法需要提供一个Supplier方法</span></span><br><span class="line">        SuppliedThreadLocal(Supplier&lt;? extends T&gt; supplier) &#123;</span><br><span class="line">            <span class="keyword">this</span>.supplier = Objects.requireNonNull(supplier);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">protected</span> T <span class="title">initialValue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> supplier.get();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<h3 id="ThreadLocalMap"><a href="#ThreadLocalMap" class="headerlink" title="ThreadLocalMap"></a>ThreadLocalMap</h3><h4 id="Entry"><a href="#Entry" class="headerlink" title="Entry"></a>Entry</h4><p>底层实现也是通过Entry数组实现的。 先看一下Entry的数据结构。ThreadLocal底层的key是WeakReference的，目的是为了防止内存泄漏。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Entry</span> <span class="keyword">extends</span> <span class="title">WeakReference</span>&lt;<span class="title">ThreadLocal</span>&lt;?&gt;&gt; </span>&#123;</span><br><span class="line">           <span class="comment">/** The value associated with this ThreadLocal. */</span></span><br><span class="line">           Object value;</span><br><span class="line"></span><br><span class="line">           Entry(ThreadLocal&lt;?&gt; k, Object v) &#123;</span><br><span class="line">               <span class="keyword">super</span>(k);</span><br><span class="line">               value = v;</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;</span><br></pre></td></tr></table></figure>

<h4 id="为什么使用weakRefernce"><a href="#为什么使用weakRefernce" class="headerlink" title="为什么使用weakRefernce"></a>为什么使用weakRefernce</h4><p>我们常听说不当使用ThreadLocal会导致内存泄漏，那ThreadLocal本身做了哪些优化来减少这种情况。</p>
<p>答案就是<strong>WeakReference</strong>。</p>
<p>当key没有外部引用的时候，这时候key就只有一个弱引用，虚拟机就会回收掉这个key。ThreadLocalMap的key为null的时候，ThreadLocalMap就会清除掉这些过时的entry。</p>
<blockquote>
<p>假设垃圾收集器在某个时间点决定一个对象是<strong>弱可达的(weakly reachable)</strong>（也就是说当前指向它的全都是弱引用），这时垃圾收集器会清除所有指向该对象的弱引用，然后垃圾收集器会把这个弱可达对象标记为可终结(finalizable)的，这样它们随后就会被回收。</p>
</blockquote>
<h4 id="主要属性"><a href="#主要属性" class="headerlink" title="主要属性"></a>主要属性</h4><p>entry是一个数组，所以容量也就是数组的长度是固定的。</p>
<p>比如 初始数组，这里的长度即INITIAL_CAPACITY = 16。这个数组里所有元素都是null，所以size = 0；</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">table = <span class="keyword">new</span> Entry[INITIAL_CAPACITY]</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 初始容量，必须是2的幂次方</span></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> INITIAL_CAPACITY = <span class="number">16</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * Entry数组</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="keyword">private</span> Entry[] table;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * Entry数组的size，即非null的entry的数量</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">int</span> size = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">   * 当size到threshold，就进行扩容</span></span><br><span class="line"><span class="comment">   * 通常这个值是数组长度的2/3</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">int</span> threshold; <span class="comment">// Default to 0</span></span><br></pre></td></tr></table></figure>

<h4 id="构造方法"><a href="#构造方法" class="headerlink" title="构造方法"></a>构造方法</h4><p>ThreadLocal只有在使用到的时候才会去创建一个ThreadLocalMap。因此就没有提供无参的构造函数。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">ThreadLocalMap</span> </span>&#123;</span><br><span class="line">      </span><br><span class="line">        ThreadLocalMap(ThreadLocal&lt;?&gt; firstKey, Object firstValue) &#123;</span><br><span class="line">          <span class="comment">// 声明一个长度为16的数组</span></span><br><span class="line">            table = <span class="keyword">new</span> Entry[INITIAL_CAPACITY];</span><br><span class="line">            <span class="keyword">int</span> i = firstKey.threadLocalHashCode &amp; (INITIAL_CAPACITY - <span class="number">1</span>);</span><br><span class="line">            table[i] = <span class="keyword">new</span> Entry(firstKey, firstValue);</span><br><span class="line">            size = <span class="number">1</span>;</span><br><span class="line">            setThreshold(INITIAL_CAPACITY);</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure>

<h5 id="可继承的ThreadLocal"><a href="#可继承的ThreadLocal" class="headerlink" title="可继承的ThreadLocal"></a>可继承的ThreadLocal</h5><p>当你的子线程创建的时候，如果需要从父线程继承ThreadLocalMap的时候，可以使用<strong>InheritedThreadLocal</strong>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> ThreadLocal&lt;Integer&gt; integerThreadLocal = <span class="keyword">new</span> InheritableThreadLocal&lt;&gt;();</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">        integerThreadLocal.set(<span class="number">1</span>);</span><br><span class="line">  <span class="comment">// 这里new线程的时候，就已经将integerThreadLocal复制到了子线程里了。</span></span><br><span class="line">        Thread thread = <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">            System.out.println(integerThreadLocal.get());</span><br><span class="line">        &#125;);</span><br><span class="line">        thread.start();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>原理很简单，就是在new Thread的时候直接去复制一份threadLocalMap，并放到子线程里。因此，在线程创建之后，父线程里ThreadLocalMap的改动对子线程是不可见的。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">         * Construct a new map including all Inheritable ThreadLocals</span></span><br><span class="line"><span class="comment">         * from given parent map. Called only by createInheritedMap.</span></span><br><span class="line"><span class="comment">         *</span></span><br><span class="line"><span class="comment">         * 在创建可继承ThreadLocalMap的时候使用。会从父线程继承ThreadLocalMap</span></span><br><span class="line"><span class="comment">         * <span class="doctag">@param</span> parentMap the map associated with parent thread.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="function"><span class="keyword">private</span> <span class="title">ThreadLocalMap</span><span class="params">(ThreadLocalMap parentMap)</span> </span>&#123;</span><br><span class="line">            Entry[] parentTable = parentMap.table;</span><br><span class="line">            <span class="keyword">int</span> len = parentTable.length;</span><br><span class="line">            setThreshold(len);</span><br><span class="line">            table = <span class="keyword">new</span> Entry[len];</span><br><span class="line"></span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; len; j++) &#123;</span><br><span class="line">              <span class="comment">// 读取父线程的Entry数组，并放到当前线程的table里</span></span><br><span class="line">                Entry e = parentTable[j];</span><br><span class="line">                <span class="keyword">if</span> (e != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</span><br><span class="line">                    ThreadLocal&lt;Object&gt; key = (ThreadLocal&lt;Object&gt;) e.get();</span><br><span class="line">                    <span class="keyword">if</span> (key != <span class="keyword">null</span>) &#123;</span><br><span class="line">                        Object value = key.childValue(e.value);</span><br><span class="line">                        Entry c = <span class="keyword">new</span> Entry(key, value);</span><br><span class="line">                        <span class="keyword">int</span> h = key.threadLocalHashCode &amp; (len - <span class="number">1</span>);</span><br><span class="line">                        <span class="keyword">while</span> (table[h] != <span class="keyword">null</span>)</span><br><span class="line">                            h = nextIndex(h, len);</span><br><span class="line">                        table[h] = c;</span><br><span class="line">                        size++;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure>

<hr>
<p>ThreadLocal的get方法具体逻辑就是在ThreadLocalMap的getEntry里。我们前面提到ThreadLocalMap使用的是<strong>开放寻址法</strong>。</p>
<p>插入的时候，根据hashcode和数组长度计算得出一个下标，当下标所对应的位置为null的时候就插入，不为null的时候就继续往后查找，直到找到一个空的位置。</p>
<p>查找的时候，同样计算得到下标，一个一个比较key，直到找到对应的entry。</p>
<h4 id="getEntry"><a href="#getEntry" class="headerlink" title="getEntry"></a>getEntry</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> Entry <span class="title">getEntry</span><span class="params">(ThreadLocal&lt;?&gt; key)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> i = key.threadLocalHashCode &amp; (table.length - <span class="number">1</span>);</span><br><span class="line">    Entry e = table[i];</span><br><span class="line">    <span class="keyword">if</span> (e != <span class="keyword">null</span> &amp;&amp; e.get() == key)</span><br><span class="line">        <span class="keyword">return</span> e;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="comment">// 当直接计算得到的下标不是要找的entry的时候</span></span><br><span class="line">        <span class="keyword">return</span> getEntryAfterMiss(key, i, e);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> Entry <span class="title">getEntryAfterMiss</span><span class="params">(ThreadLocal&lt;?&gt; key, <span class="keyword">int</span> i, Entry e)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 局部变量, 在字节码层面效率会更高一点</span></span><br><span class="line">            Entry[] tab = table;</span><br><span class="line">            <span class="keyword">int</span> len = tab.length;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">while</span> (e != <span class="keyword">null</span>) &#123;</span><br><span class="line">                ThreadLocal&lt;?&gt; k = e.get();</span><br><span class="line">                <span class="keyword">if</span> (k == key)</span><br><span class="line">                    <span class="keyword">return</span> e;</span><br><span class="line">                <span class="comment">//　entry不为空，但是key被回收了</span></span><br><span class="line">                <span class="keyword">if</span> (k == <span class="keyword">null</span>)</span><br><span class="line">                    <span class="comment">// 移除失效的entry，避免内存泄漏</span></span><br><span class="line">                    expungeStaleEntry(i);</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    <span class="comment">// 下一个index</span></span><br><span class="line">                    i = nextIndex(i, len);</span><br><span class="line">                e = tab[i];</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure>

<p>移除失效的entry是为了避免线程不再使用的ThreadLocal对象长期存在与ThreadLocalMap中，导致内存溢出。如果使用普通的引用，那么ThreadLocalMap中已经失效的变量，很有可能经过了好几轮的垃圾回收，还存在于线程里，所以ThreadLocal使用的弱引用，当只有线程thread持有这个ThreadLocal的时候，就将key回收掉，然后在get，set方法里再回收掉key为null的entry。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">expungeStaleEntry</span><span class="params">(<span class="keyword">int</span> staleSlot)</span> </span>&#123;</span><br><span class="line">            Entry[] tab = table;</span><br><span class="line">            <span class="keyword">int</span> len = tab.length;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 将无用的entry置空</span></span><br><span class="line">            tab[staleSlot].value = <span class="keyword">null</span>;</span><br><span class="line">            tab[staleSlot] = <span class="keyword">null</span>;</span><br><span class="line">            size--;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 当碰到null值的时候，整理一遍entry数组</span></span><br><span class="line">            Entry e;</span><br><span class="line">            <span class="keyword">int</span> i;</span><br><span class="line">    	 		  <span class="comment">// 一直往后检查， 直到检查到null的位置，检查结束</span></span><br><span class="line">            <span class="keyword">for</span> (i = nextIndex(staleSlot, len);</span><br><span class="line">                 (e = tab[i]) != <span class="keyword">null</span>;</span><br><span class="line">                 i = nextIndex(i, len)) &#123;</span><br><span class="line">                ThreadLocal&lt;?&gt; k = e.get();</span><br><span class="line">                <span class="comment">// key为null的情况，清除entry</span></span><br><span class="line">                <span class="keyword">if</span> (k == <span class="keyword">null</span>) &#123;</span><br><span class="line">                    e.value = <span class="keyword">null</span>;</span><br><span class="line">                    tab[i] = <span class="keyword">null</span>;</span><br><span class="line">                    size--;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="comment">// 重新计算一遍下标，看看是不是被挤到这个位置的</span></span><br><span class="line">                    <span class="comment">// 如果不是直接set的，而是被挤到这里的，那么就要重新找位置</span></span><br><span class="line">                    <span class="keyword">int</span> h = k.threadLocalHashCode &amp; (len - <span class="number">1</span>);</span><br><span class="line">                    <span class="keyword">if</span> (h != i) &#123;</span><br><span class="line">                        tab[i] = <span class="keyword">null</span>;</span><br><span class="line">                        <span class="comment">// 当查到tab[h]为null的，说明这个h下标的entry已经被移除了</span></span><br><span class="line">                        <span class="keyword">while</span> (tab[h] != <span class="keyword">null</span>)</span><br><span class="line">                            h = nextIndex(h, len);</span><br><span class="line">                        tab[h] = e;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> i;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure>

<h4 id="set"><a href="#set" class="headerlink" title="set"></a>set</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">set</span><span class="params">(ThreadLocal&lt;?&gt; key, Object value)</span> </span>&#123;</span><br><span class="line">            Entry[] tab = table;</span><br><span class="line">            <span class="keyword">int</span> len = tab.length;</span><br><span class="line">  <span class="comment">// 计算下标值</span></span><br><span class="line">            <span class="keyword">int</span> i = key.threadLocalHashCode &amp; (len-<span class="number">1</span>);</span><br><span class="line"><span class="comment">// 直接开始遍历数组</span></span><br><span class="line">            <span class="keyword">for</span> (Entry e = tab[i];</span><br><span class="line">                 e != <span class="keyword">null</span>;</span><br><span class="line">                 e = tab[i = nextIndex(i, len)]) &#123;</span><br><span class="line">                ThreadLocal&lt;?&gt; k = e.get();</span><br><span class="line"><span class="comment">// 找到了对应的key</span></span><br><span class="line">                <span class="keyword">if</span> (k == key) &#123;</span><br><span class="line">                    e.value = value;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line"><span class="comment">// 从key为null的地方往前遍历，查找key</span></span><br><span class="line">                <span class="keyword">if</span> (k == <span class="keyword">null</span>) &#123;</span><br><span class="line">                    replaceStaleEntry(key, value, i);</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            tab[i] = <span class="keyword">new</span> Entry(key, value);</span><br><span class="line">            <span class="keyword">int</span> sz = ++size;</span><br><span class="line">  <span class="comment">// 清除操作/整理</span></span><br><span class="line">  <span class="comment">// cleanSomeSlots返回值代表是否成功清除过期entry</span></span><br><span class="line">            <span class="keyword">if</span> (!cleanSomeSlots(i, sz) &amp;&amp; sz &gt;= threshold)</span><br><span class="line">                rehash();</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">replaceStaleEntry</span><span class="params">(ThreadLocal&lt;?&gt; key, Object value,</span></span></span><br><span class="line"><span class="function"><span class="params">                                       <span class="keyword">int</span> staleSlot)</span> </span>&#123;</span><br><span class="line">            Entry[] tab = table;</span><br><span class="line">            <span class="keyword">int</span> len = tab.length;</span><br><span class="line">            Entry e;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 避免垃圾回收产生的空位置，所以这里从staleSlot往前走一圈</span></span><br><span class="line">  					<span class="comment">// 直到找到一个空位置</span></span><br><span class="line">            <span class="keyword">int</span> slotToExpunge = staleSlot;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> i = prevIndex(staleSlot, len);</span><br><span class="line">                 (e = tab[i]) != <span class="keyword">null</span>;</span><br><span class="line">                 i = prevIndex(i, len))</span><br><span class="line">                <span class="comment">// get获取到的是key</span></span><br><span class="line">                <span class="keyword">if</span> (e.get() == <span class="keyword">null</span>)</span><br><span class="line">                    slotToExpunge = i;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 从staleSlot开始遍历查找</span></span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> i = nextIndex(staleSlot, len);</span><br><span class="line">                 (e = tab[i]) != <span class="keyword">null</span>;</span><br><span class="line">                 i = nextIndex(i, len)) &#123;</span><br><span class="line">                ThreadLocal&lt;?&gt; k = e.get();</span><br><span class="line"></span><br><span class="line">                <span class="comment">// key相同，为了不rehash，和废弃的entry交换位置</span></span><br><span class="line">                <span class="keyword">if</span> (k == key) &#123;</span><br><span class="line">                    e.value = value;</span><br><span class="line">	</span><br><span class="line">                    tab[i] = tab[staleSlot];</span><br><span class="line">                    tab[staleSlot] = e;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 开始整理废弃的entry</span></span><br><span class="line">                    <span class="keyword">if</span> (slotToExpunge == staleSlot)</span><br><span class="line">                        slotToExpunge = i;</span><br><span class="line">                  <span class="comment">// 清除所有废弃的entry，并且重新hash</span></span><br><span class="line">                    cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (k == <span class="keyword">null</span> &amp;&amp; slotToExpunge == staleSlot)</span><br><span class="line">                    slotToExpunge = i;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 没找到key，就直接替换废弃的entry</span></span><br><span class="line">            tab[staleSlot].value = <span class="keyword">null</span>;</span><br><span class="line">            tab[staleSlot] = <span class="keyword">new</span> Entry(key, value);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 如果在这个过程中又产生了新的废弃entry，则重新回收一遍</span></span><br><span class="line">            <span class="keyword">if</span> (slotToExpunge != staleSlot)</span><br><span class="line">                cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">cleanSomeSlots</span><span class="params">(<span class="keyword">int</span> i, <span class="keyword">int</span> n)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">boolean</span> removed = <span class="keyword">false</span>;</span><br><span class="line">    Entry[] tab = table;</span><br><span class="line">    <span class="keyword">int</span> len = tab.length;</span><br><span class="line">  <span class="comment">// 从i开始扫描log2n次</span></span><br><span class="line">  <span class="comment">// 如果碰到了脏的entry，则继续扫描log2n次</span></span><br><span class="line">    <span class="keyword">do</span> &#123;</span><br><span class="line">        i = nextIndex(i, len);</span><br><span class="line">        Entry e = tab[i];</span><br><span class="line">        <span class="keyword">if</span> (e != <span class="keyword">null</span> &amp;&amp; e.get() == <span class="keyword">null</span>) &#123;</span><br><span class="line">            n = len;</span><br><span class="line">            removed = <span class="keyword">true</span>;</span><br><span class="line">            i = expungeStaleEntry(i);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">while</span> ( (n &gt;&gt;&gt;= <span class="number">1</span>) != <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">return</span> removed;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://arrycoder.gitee.io/blog/并发/可见性和有序性/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="arry">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/blog/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="arry's blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/并发/可见性和有序性/" itemprop="url">可见性/有序性</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Posted on</span>
              
              <time title="Post created" itemprop="dateCreated datePublished" datetime="2020-06-02T10:30:26+08:00">
                2020-06-02
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">In</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/并发/" itemprop="url" rel="index">
                    <span itemprop="name">并发</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          <h1 id="有序性和可见性"><a href="#有序性和可见性" class="headerlink" title="有序性和可见性"></a>有序性和可见性</h1><p>可见性是指，其他线程可以立马赶知道当前线程对共享数据的改动。</p>
          <!--noindex-->
          <div class="post-button text-center">
            <a class="btn" href="/blog/并发/可见性和有序性/#more" rel="contents">
              Read more &raquo;
            </a>
          </div>
          <!--/noindex-->
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://arrycoder.gitee.io/blog/并发/原子性/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="arry">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/blog/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="arry's blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/并发/原子性/" itemprop="url">原子性</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Posted on</span>
              
              <time title="Post created" itemprop="dateCreated datePublished" datetime="2020-05-27T09:50:43+08:00">
                2020-05-27
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">In</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/并发/" itemprop="url" rel="index">
                    <span itemprop="name">并发</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          <h1 id="原子-Atomic"><a href="#原子-Atomic" class="headerlink" title="原子(Atomic)"></a>原子(Atomic)</h1><p>保证一个对象只有一个线程能对其进行操作（互斥访问）</p>
          <!--noindex-->
          <div class="post-button text-center">
            <a class="btn" href="/blog/并发/原子性/#more" rel="contents">
              Read more &raquo;
            </a>
          </div>
          <!--/noindex-->
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://arrycoder.gitee.io/blog/MessageQueue/RocketMQ/架构介绍/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="arry">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/blog/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="arry's blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/MessageQueue/RocketMQ/架构介绍/" itemprop="url">架构介绍</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Posted on</span>
              
              <time title="Post created" itemprop="dateCreated datePublished" datetime="2020-05-26T11:35:56+08:00">
                2020-05-26
              </time>
            

            

            
          </span>

          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            
          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://arrycoder.gitee.io/blog/MessageQueue/RocketMQ/NameServer/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="arry">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/blog/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="arry's blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/MessageQueue/RocketMQ/NameServer/" itemprop="url">NameServer</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Posted on</span>
              
              <time title="Post created" itemprop="dateCreated datePublished" datetime="2020-05-26T11:14:54+08:00">
                2020-05-26
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">In</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/MQ/" itemprop="url" rel="index">
                    <span itemprop="name">MQ</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h1 id="NameServer"><a href="#NameServer" class="headerlink" title="NameServer"></a>NameServer</h1><p>名称服务充当路由消息的提供者。生产者或消费者能够通过名字服务查找各主题相应的Broker IP列表。多个Namesrv实例组成集群，但相互独立，没有信息交换。</p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://arrycoder.gitee.io/blog/并发/内存模型/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="arry">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/blog/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="arry's blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/并发/内存模型/" itemprop="url">JVM内存模型</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Posted on</span>
              
              <time title="Post created" itemprop="dateCreated datePublished" datetime="2020-05-22T16:35:16+08:00">
                2020-05-22
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">In</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/并发/" itemprop="url" rel="index">
                    <span itemprop="name">并发</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          <h1 id="JVM内存模型"><a href="#JVM内存模型" class="headerlink" title="JVM内存模型"></a>JVM内存模型</h1><p>因为计算机的不断发展，程序要怎样尽量的发挥出计算机最大的性能，也成为了一个值得不断研究的问题。</p>
          <!--noindex-->
          <div class="post-button text-center">
            <a class="btn" href="/blog/并发/内存模型/#more" rel="contents">
              Read more &raquo;
            </a>
          </div>
          <!--/noindex-->
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://arrycoder.gitee.io/blog/并发/锁/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="arry">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/blog/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="arry's blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/并发/锁/" itemprop="url">锁</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Posted on</span>
              
              <time title="Post created" itemprop="dateCreated datePublished" datetime="2020-05-22T16:13:02+08:00">
                2020-05-22
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">In</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/并发/" itemprop="url" rel="index">
                    <span itemprop="name">并发</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h1 id="锁"><a href="#锁" class="headerlink" title="锁"></a>锁</h1><h2 id="Synchronized"><a href="#Synchronized" class="headerlink" title="Synchronized"></a>Synchronized</h2><p>需要指定锁定的对象，如果没指定的话，就根据方法是静态方法还是普通方法来锁定对应的Class或者对象。原理是在访问该方法字节码的前后加上 monitorenter 和 monitorexit。</p>
<p>在碰到monitorenter指令的时候，尝试去获取锁，如果线程本身已经持有锁 ，则计数器+1，monitorend的时候计数器-1，当计数器为0的时候释放锁。</p>
<p>阻塞和唤醒线程需要从 用户态 转换到 内核态，有可能这个转换时间，比用户的代码执行时间还长。所以synchronized属于一种重量级的操作，JVM本身也加入了一些优化，比如自旋锁、自适应自旋锁，避免线程频繁切换状态。</p>
<h5 id="锁优化"><a href="#锁优化" class="headerlink" title="锁优化"></a>锁优化</h5><ul>
<li><p>自旋锁</p>
<p>线程的挂起和唤醒需要切到内核态中去完成，所以在阻塞的时候会浪费很多资源去完成这些操作。因此在获取锁失败的时候，会让线程空循环一会儿，看看其他线程是否会释放掉锁。因为自旋锁不断做空循环也是会占用执行时间的，所以在自旋一定次数之后还没获取到锁，就按照原本的方式挂起线程并等待唤醒。</p>
<p>同时1.6之后还提供了一个自适应的自旋锁，如果一个线程自旋等待之后获取到了锁，就在下次获取锁的时候加大循环次数，而如果对于一个锁很少获取成功，那么在之后可能就会省去自旋这个步骤，避免浪费处理器资源。</p>
</li>
<li><p>锁消除</p>
<p>经过逃逸分析之后，发现堆上数据不会被其他线程共享，就可以当作是线程私有的数据，无需加同步操作。</p>
</li>
<li><p>锁粗化</p>
<p>在写代码的时候，通常会将锁的粒度写的小一点，这样即使存在锁竞争，也会尽快的释放锁。但是如果存在对同一个对象反复的加锁解锁，即使没有锁竞争也会导致不必要的性能损耗。虚拟机如果检测到这种情况，就会将锁粗化到整个操作序列的外部。</p>
</li>
<li><p>轻量级锁</p>
<p>在虚拟机的对象头里有两部分信息，一部分是对象自身的运行时数据，官方称为<strong>Mark word</strong>，另一个部分用于存储指向方法区对象类型数据的指针，数组对象的话还用于存储数组长度。轻量级锁和偏向锁的实现都是基于Mark Word。</p>
<p>对于32位虚拟机来说，一个无锁对象25位用于存储hashCode，4位存储gc年龄，1位存储偏向锁，剩下两位用来存储锁标志。虚拟机为了节省空间，根据锁标志位的不同，mark word的值也不相同。</p>
</li>
</ul>
<table>
<thead>
<tr>
<th align="center">biased_lock（偏向锁标记）</th>
<th align="center">lock（锁标志）</th>
<th align="center">状态</th>
</tr>
</thead>
<tbody><tr>
<td align="center">0</td>
<td align="center">01</td>
<td align="center">无锁</td>
</tr>
<tr>
<td align="center">1</td>
<td align="center">01</td>
<td align="center">偏向锁</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">00</td>
<td align="center">轻量级锁</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">10</td>
<td align="center">重量级锁</td>
</tr>
<tr>
<td align="center">0</td>
<td align="center">11</td>
<td align="center">GC标记</td>
</tr>
</tbody></table>
<p>当一个线程需要对一个对象进行加锁的时候会在栈帧上开辟一块锁记录（Lock Record）的空间，用来存储对象的mark word的拷贝，然后通过CAS将对象头更新为当前线程的指针，并将锁记录更新为00，如果CAS更新失败，再判断对象的指针是否指向自己，如果没指向说明存在着锁竞争，需要膨胀为重量级锁，后面再获取锁的线程都需要进入阻塞状态。</p>
<p><img src="/blog/并发/锁/lightweight.png" alt="轻量级锁膨胀过程"></p>
<ul>
<li><p>偏向锁</p>
<p>偏向锁的目的是为了消除无竞争状态下的同步操作。线程在获取锁的时候，会判断当前对象的mark word里是否有当前线程的id，如果有的话，直接拿来用，省去了CAS的操作。而当另外一个线程尝试获取锁的时候，就会根据当前对象的锁状态来恢复成无锁状态或者升级成轻量级锁。</p>
</li>
</ul>
<p>附上32位虚拟机对象头在不同阶段的结构</p>
<p><strong><em>biased_lock</em></strong>：对象是否启用偏向锁标记，只占1个二进制位。为1时表示对象启用偏向锁，为0时表示对象没有偏向锁。<br> <strong><em>age</em></strong>：4位的Java对象年龄。<br> <strong><em>identity_hashcode</em></strong>：25位的对象标识Hash码，采用延迟加载技术。调用方法<code>System.identityHashCode()</code>计算，并会将结果写到该对象头中。当对象被锁定时，该值会移动到管程Monitor中。<br> <strong><em>thread</em></strong>：持有偏向锁的线程ID。<br> <strong><em>epoch</em></strong>：偏向时间戳。<br> <strong><em>ptr_to_lock_record</em></strong>：指向栈中锁记录的指针。<br> <strong><em>ptr_to_heavyweight_monitor</em></strong>：指向管程Monitor的指针</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">|-------------------------------------------------------|--------------------|</span><br><span class="line">|                  <span class="function">Mark <span class="title">Word</span> <span class="params">(<span class="number">32</span> bits)</span>                  |       State        |</span></span><br><span class="line"><span class="function">|-------------------------------------------------------|--------------------|</span></span><br><span class="line"><span class="function">| identity_hashcode:25 | age:4 | biased_lock:1 | lock:2 |       Normal       |</span></span><br><span class="line"><span class="function">|-------------------------------------------------------|--------------------|</span></span><br><span class="line"><span class="function">|  thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:2 |       Biased       |</span></span><br><span class="line"><span class="function">|-------------------------------------------------------|--------------------|</span></span><br><span class="line"><span class="function">|               ptr_to_lock_record:30          | lock:2 | Lightweight Locked |</span></span><br><span class="line"><span class="function">|-------------------------------------------------------|--------------------|</span></span><br><span class="line"><span class="function">|               ptr_to_heavyweight_monitor:30  | lock:2 | Heavyweight Locked |</span></span><br><span class="line"><span class="function">|-------------------------------------------------------|--------------------|</span></span><br><span class="line"><span class="function">|                                              | lock:2 |    Marked <span class="keyword">for</span> GC   |</span></span><br><span class="line"><span class="function">|-------------------------------------------------------|--------------------|</span></span><br></pre></td></tr></table></figure>

<h2 id="ReentrantLock"><a href="#ReentrantLock" class="headerlink" title="ReentrantLock"></a>ReentrantLock</h2><p>相对于synchronized，ReentrantLock额外提供了 <strong>可中断等待</strong>，<strong>可实现公平锁</strong>，<strong>锁绑定多个条件</strong>三个功能</p>
<ul>
<li><p>可中断等待</p>
<p>内置锁synchronized阻塞的时候是无法中断的，ReentrantLock则提供了<strong><em>lockInterruptibly</em></strong>方法，在等待锁的时候如果收到了interrupt请求，则会中断阻塞状态，抛出异常。除此之外还提供了<strong><em>tryLock</em></strong>方法，在超时时间过后就直接放弃获取锁。</p>
</li>
<li><p>公平锁和非公平锁</p>
<p>多个线程竞争锁的时候，如果按获取锁的位置排队，就是公平锁。非公平锁会先尝试插队，插队失败了再获取锁。ReentrantLock提供了公平锁和非公平锁两种实现，而synchronized关键字只支持非公平锁。</p>
</li>
<li><p>锁绑定多个条件</p>
<p>ReentrantLock使用以下方法，新建多个conditionList</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Condition condition1 = lock.newCondition();</span><br><span class="line">Condition condition2 = lock.newCondition();</span><br></pre></td></tr></table></figure>

</li>
</ul>
<h3 id="AQS"><a href="#AQS" class="headerlink" title="AQS"></a>AQS</h3><p>使用双向链表来完成资源获取的排队工作，通过CAS对state值的修改来确认锁的获取。</p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://arrycoder.gitee.io/blog/jvm/类加载过程/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="arry">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/blog/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="arry's blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/jvm/类加载过程/" itemprop="url">类加载过程</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Posted on</span>
              
              <time title="Post created" itemprop="dateCreated datePublished" datetime="2020-05-20T17:23:30+08:00">
                2020-05-20
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">In</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/JVM/" itemprop="url" rel="index">
                    <span itemprop="name">JVM</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          <p>[TOC]</p>
<h1 id="类加载过程"><a href="#类加载过程" class="headerlink" title="类加载过程"></a>类加载过程</h1>
          <!--noindex-->
          <div class="post-button text-center">
            <a class="btn" href="/blog/jvm/类加载过程/#more" rel="contents">
              Read more &raquo;
            </a>
          </div>
          <!--/noindex-->
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://arrycoder.gitee.io/blog/jvm/collector/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="arry">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/blog/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="arry's blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/jvm/collector/" itemprop="url">垃圾收集器</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Posted on</span>
              
              <time title="Post created" itemprop="dateCreated datePublished" datetime="2020-05-18T16:18:59+08:00">
                2020-05-18
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">In</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/JVM/" itemprop="url" rel="index">
                    <span itemprop="name">JVM</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h2 id="JVM的内存管理"><a href="#JVM的内存管理" class="headerlink" title="JVM的内存管理"></a>JVM的内存管理</h2><p>Java将内存管理的权力交给了JVM自己，因此我们在写代码的时候，可以不花精力在内存分配，垃圾回收这些需求之外的事情上。这样的好处显而易见，降低了编程的复杂度，提高了我们效率，也因为如此，大部分的对JVM垃圾收集器不熟悉的程序员，在经历JVM频繁GC的时候会无从下手。</p>
<h3 id="jvm运行时内存"><a href="#jvm运行时内存" class="headerlink" title="jvm运行时内存"></a>jvm运行时内存</h3><ul>
<li>程序计数器<ul>
<li>用来记录程序执行到哪一行，字节码解释器会通过这个值来决定下一条要执行的字节码。一个处理器只会执行一个线程中的字节码，因此每个线程都会有一块内存用来储存这个计数器的值，当线程切换后能恢复到正确的执行位置。</li>
</ul>
</li>
<li>虚拟机栈<ul>
<li>Java中每个方法的执行，都是一个栈帧入栈出栈的过程。栈帧上存储着局部变量表，局部变量表里则存着基本数据类型和对象引用。</li>
<li>栈帧里还记录着方法的入口，方法的返回，动态链接和操作数栈</li>
</ul>
</li>
<li>本地方法栈<ul>
<li>和虚拟机栈功能差不多，只不过一个执行的是字节码，一个执行的是本地方法。</li>
</ul>
</li>
<li>堆<ul>
<li>用来存储对象实例的区域，也是内存管理最主要的工作区间。</li>
<li>通常所有实例对象都存储在堆上面，但是因为逃逸分析和TLAB，这件事并不是那么绝对<ul>
<li>逃逸分析<br>没有逃逸的对象，可以直接在栈上分配。如果一个对象只给一个线程访问，就可以直接访问，无需加同步保护，可以提升效率（被赋值给全局变量，当作返回值返回，或者当作参数传递给其他方法）​​</li>
<li>TLAB（Thread-local allocation buffer）<br>在eden区中，给每一个线程开辟一块区域，小对象会默认存放在这块区域。因为TLAB是线程私有的，所以免去了锁开销。​​</li>
</ul>
</li>
<li>对象分配的过程<ul>
<li>1.TLAB区如果空间足够，则直接分配</li>
<li>2.TLAB空间不足，就多开一块TLAB空间来分配</li>
<li>3.如果仍然不足，则对eden区加锁，分配对象</li>
<li>4.eden区不足，则进行一次youngGC</li>
<li>5.youngGC后仍然不足，就直接分配到老年代（担保分配）</li>
</ul>
</li>
</ul>
</li>
<li>方法区<ul>
<li>存放已经被加载的类信息，常量池，静态变量和即使编译器编译之后的代码。方法区也被叫做永久代，主要是因为垃圾回收器也会在这块区域工作，主要负责的是类的卸载和常量池的回收。Java8之后将方法区的实现移到了堆外内存里，主要是为了避免方法区产生OOM。但是堆外内存也是受计算机本身的内存限制的，所以在虚拟机启动的时候最好也要指定一下MaxMetaSpaceSize，来防止无限制的扩容引起操作系统的kill操作。</li>
</ul>
</li>
<li>运行时常量池<ul>
<li>属于方法区的一部分。Class文件除了类版本，字段，方法，接口描述信息等，还有一块常量池，用来存放编译期产生的各种字面量和符号引用，在类加载之后，这块内容会存放到运行时常量池里。</li>
<li>运行时常量池是动态的，所以在运行期间，也可以将内容存放到池中。</li>
</ul>
</li>
</ul>
<h2 id="垃圾回收"><a href="#垃圾回收" class="headerlink" title="垃圾回收"></a>垃圾回收</h2><p>垃圾回收，主要针对的是堆和方法区，因为这两块区域是所有线程共享的，在两块区域上存储的东西都是动态的，无法预测的，所以在程序运行过程中，就需要回收掉没用的“垃圾”，并且负责给新的对象分配空间。</p>
<h3 id="哪些对象需要被回收"><a href="#哪些对象需要被回收" class="headerlink" title="哪些对象需要被回收"></a>哪些对象需要被回收</h3><h4 id="引用计数法"><a href="#引用计数法" class="headerlink" title="引用计数法"></a>引用计数法</h4><p>这是一种比较简单的判断方法，当一个对象被引用一次的时候，就加1，如果这个对象被引用的次数为0的时候，说明该对象可以被回收了。这种方法的优点是 判断效率高，算法简单，但是无法解决循环依赖，当出现环状引用的时候，就会导致内存泄漏，无法回收这些对象。</p>
<h4 id="GC-Root"><a href="#GC-Root" class="headerlink" title="GC Root"></a>GC Root</h4><p>根搜索算法是指通过一系列称为GC Root的对象往下搜索，所有走过的路径叫做引用链，当搜索完成之后，所有不可达的对象就会被判定成需要回收的对象。</p>
<p>可作为GC Root的对象有</p>
<ol>
<li>栈帧中引用的对象</li>
<li>静态属性引用的对象</li>
<li>常量引用的对象</li>
<li>Native方法引用的对象</li>
</ol>
<h4 id="引用"><a href="#引用" class="headerlink" title="引用"></a>引用</h4><ul>
<li><p>强引用：类似Object obj = new Object()；只要有强引用，那么该对象就不会被回收</p>
</li>
<li><p>软引用：只有当内存即将溢出的时候，才会将软引用作二次回收</p>
</li>
<li><p>弱引用（Weak Reference）：只能生存到下一次垃圾回收。</p>
</li>
<li><p>虚引用 ：这个引用对于对象生命周期来说没有任何影响</p>
</li>
</ul>
<h4 id="方法区的回收"><a href="#方法区的回收" class="headerlink" title="方法区的回收"></a>方法区的回收</h4><ol>
<li>常量没有被引用的时候就可以回收了</li>
<li>Class<ol>
<li>堆中已经没有该Class的实例</li>
<li>加载该类的ClassLoader已经被回收</li>
<li>对应的Class对象没有被引用，无法在任何地方通过反射调用该类</li>
</ol>
</li>
</ol>
<h3 id="垃圾回收算法"><a href="#垃圾回收算法" class="headerlink" title="垃圾回收算法"></a>垃圾回收算法</h3><h4 id="复制"><a href="#复制" class="headerlink" title="复制"></a>复制</h4><p>将内存分为两个区域，当一个区域无法分配内存的时候，把一个区域的对象全部复制到另外一块内存里。这个算法实现简单，运行高效，但是在内存中总会有一块区域无法使用。这种算法主要是用于新生代里，因为新生代的对象生命周期都很短，所以不需要一半一半的内存分配，而是将内存分成 survivor和eden区域，survivor又分成 from survivor 和 to survivor，一次垃圾回收的流程就是将所有 from和eden区域的所有对象分配到 to survivor区域里，最后清理掉eden和 from survivor区域。survivor和eden区域默认比例是2/8，因此每次新生代可用内存都是 90%。</p>
<p>当另一块survivor区域的大小不够放的时候，就会有内存分配担保机制来保证新对象可以正常分配。在分配之前会先判断老年代是否有足够的连续空间用来存放这些对象，如果够的话，就直接将这些对象放到老年代，如果老年代也不够的话，那么就直接发起一次full gc。</p>
<h4 id="标记-清除"><a href="#标记-清除" class="headerlink" title="标记-清除"></a>标记-清除</h4><p>标记出所有需要回收的对象，然后在回收这些对象。这个算法的全局标记效率太低，同时会产生大量的内存碎片，当分配大对象的时候会导致不得不再触发一次垃圾回收。</p>
<h4 id="标记-整理"><a href="#标记-整理" class="headerlink" title="标记-整理"></a>标记-整理</h4><p>标记所有需要回收的对象，然后将所有存活的对象向一段移动，这样就保证不会有内存碎片</p>
<h4 id="分代收集算法"><a href="#分代收集算法" class="headerlink" title="分代收集算法"></a>分代收集算法</h4><p>因为老年代和新生代数据生命周期的差异，所以将堆分为老年代和新生代，对不同区域使用不同的垃圾回收器。在新生代中，因为每次gc存活的对象都很少，所以可以直接使用复制算法，只需少量的复制成本和空间，就能完成GC。而老年代因为对象存活率高，同时没有额外的空间作分配担保，所以必须使用标记-清除/ 标记-整理算法。</p>
<h3 id="垃圾回收器"><a href="#垃圾回收器" class="headerlink" title="垃圾回收器"></a>垃圾回收器</h3><h5 id="Serial"><a href="#Serial" class="headerlink" title="Serial"></a>Serial</h5><p>串行，单线程收集器，停止用户线程执行垃圾收集。</p>
<h5 id="ParNew"><a href="#ParNew" class="headerlink" title="ParNew"></a>ParNew</h5><p>并行，Serial多线程版本，仍然需要stop the world来执行垃圾收集。可以与CMS搭配使用。</p>
<h5 id="ParallelScavenge"><a href="#ParallelScavenge" class="headerlink" title="ParallelScavenge"></a>ParallelScavenge</h5><p>并行的垃圾收集器，目的是为了达到一个可控的吞吐率。</p>
<p>吞吐率 = 运行用户代码时间 / （运行用户代码时间 + 垃圾手机时间）。高吞吐率的收集器可以高效率的利用CPU，ParallelScavenge通过两个参数控制吞吐率，分别是控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis参数以及直接设置吞吐量大小的-XX:GCTimeRatio参数。</p>
<h5 id="Serial-old"><a href="#Serial-old" class="headerlink" title="Serial old"></a>Serial old</h5><p>Serial 的老年代版本</p>
<h5 id="Parallel-Old"><a href="#Parallel-Old" class="headerlink" title="Parallel Old"></a>Parallel Old</h5><p>搭配ParallelScavenge使用的并发收集器</p>
<h5 id="CMS（concurrent-mark-sweep）"><a href="#CMS（concurrent-mark-sweep）" class="headerlink" title="CMS（concurrent mark sweep）"></a>CMS（concurrent mark sweep）</h5><p>初始标记（stw）-&gt;并发标记-&gt;重新标记-&gt;并发清除。</p>
<p>初始标记的时候先直接标记gc root，然后并发标记的线程和用户线程一起工作，等并发标记结束之后，再stw重新标记，最后与用户线程并发清除掉这些垃圾。</p>
<ul>
<li><p>Foreground collector</p>
<p>空间不够就执行，不压缩</p>
</li>
<li><p>Background collector</p>
<p>不断扫描，当满足条件的时候就执行，默认是2s。</p>
</li>
</ul>
<p>CMS的缺点</p>
<ol>
<li>使用的是标记-清除算法，会产生大量的内存碎片。</li>
<li>因为与用户线程是并发的，所以对CPU资源是敏感的，当cpu小于4个的时候，会占用一半的资源去做垃圾回收，这对系统运行效率影响是很大的。</li>
<li>因为CMS的标记是和用户线程并发的，所以在cms运行的时候需要预留一部分空间给程序，但是如果预留的空间还不满足程序需求的话，这时候就会临时启用Serial Old来进行一次老年代收集。</li>
</ol>
<h5 id="G1"><a href="#G1" class="headerlink" title="G1"></a>G1</h5><p>Garbage First并不像其他垃圾收集器一样，将堆划分为新生代老年代，而是分为一个个region，region类型有四种 E  S O （eden, survivor, old）Humongous属于old区的一种，humongous（巨大）是其他垃圾收集器没有的概念，这是当分配的对象大于region的一个半的时候，直接分配在Humongous里，当分配的对象大于一个h区的时候，会申请连续的两个h区用来分配。</p>
<h5 id="YoungGC"><a href="#YoungGC" class="headerlink" title="YoungGC"></a>YoungGC</h5><p>对象直接分配到eden区，当eden区不够的时候触发young GC，把e区的数据移到s区，当s区不够的时候直接将eden数据移到o区。S区的对象达到一定年龄也会晋升到o区。</p>
<p>根是young gen，按照young gen个数来控制暂停时间。</p>
<h5 id="MixGC"><a href="#MixGC" class="headerlink" title="MixGC"></a>MixGC</h5><p>根是young gen + 根据统计时间，选出收益高的若干old gen region</p>
<p>在youngGC的时候发现老年代的内存占用达到了threshold，就开始并发标记，当内存分配速度大于MixGC的时候，就会降级成SerialOld收集。</p>
<ul>
<li><p>全局并发标记阶段</p>
<ol>
<li><strong>初始标记</strong> 在youngGC的时候，初始标记gc root直接可达的对象。这里stw是跟着youngGC来的，没有额外的暂停。</li>
<li><strong>并发标记</strong> 不断从扫描栈里取引用递归整个堆，标记所有可达对象，和youngGC同时进行的。在并发标记阶段，如果有引用产生变化，SATB的pre-write-barrier会将这些变化记录下来，collector在收集过程中也会扫描这些引用。</li>
<li><strong>重新标记</strong> stw，标记那些并发标记过程中产生变化而尚未处理的对象（SATB write-barrier），同时在这里处理弱引用。</li>
<li>清理，把没有存活对象的region回收到可分配region列表里。</li>
</ol>
</li>
<li><p>Evacuation阶段（复制）</p>
<p>把一部分region里还存活的对象复制到空region里，然后把原来的region回收。这里会根据用户配置选择合适的region进行回收，而不是把所有region都回收了。</p>
</li>
</ul>
<h2 id="SATB（snapshot-at-beginning）"><a href="#SATB（snapshot-at-beginning）" class="headerlink" title="SATB（snapshot-at-beginning）"></a>SATB（snapshot-at-beginning）</h2><p>当GC开始的时候，已经存活的对象就被认为是活的，这时候的对象图形成一个逻辑快照。同时在pre-write-barrier中，把每次引用关系的改变纪录下来。</p>
<h3 id="三色标记算法"><a href="#三色标记算法" class="headerlink" title="三色标记算法"></a>三色标记算法</h3><p>白色：未扫描或者没被标记到的对象集合</p>
<p>灰色：当前对象已被标记，但是引用尚未标记</p>
<p>黑色：当前对象和引用都已被标记    </p>
<p>因为G1是并发的，垃圾回收线程和mutator是同时执行的，所以要避免两种情况同时发生。</p>
<ol>
<li>把白色对象存到黑色对象的引用里</li>
<li>白色对象丢失了一个灰色对象对其的引用</li>
</ol>
<p>因为黑对象已经被处理过，所以不会再对他进行处理，这时候，白对象丢失了所有灰对象对其的引用，那么就会导致这个白色对象最后扫描不到而被回收。</p>
<p>因此SATB在mark开始的时候，就把所有旧的引用都变成非白的（原本就黑灰的不动，白色的就变成灰色的）。这样就避免了2</p>
<p>在CMS里，当将白色对象分配给黑色对象的时候，就会把该对象变成灰色。避免1</p>
<h3 id="RememberSet"><a href="#RememberSet" class="headerlink" title="RememberSet"></a>RememberSet</h3><p>Remembered Set是在实现部分垃圾收集（partial GC）时用于记录从<strong>非收集部分</strong>指向<strong>收集部分</strong>的指针的<strong>集合</strong>的抽象数据结构。在G1中，每个eden region都有一个RSet，包含了老年代对其的引用。</p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://arrycoder.gitee.io/blog/git/rebase/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="arry">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/blog/uploads/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="arry's blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/git/rebase/" itemprop="url">rebase</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Posted on</span>
              
              <time title="Post created" itemprop="dateCreated datePublished" datetime="2020-05-08T11:34:27+08:00">
                2020-05-08
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">In</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/git/" itemprop="url" rel="index">
                    <span itemprop="name">git</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            
          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
  </section>

  
  <nav class="pagination">
    <a class="extend prev" rel="prev" href="/blog/"><i class="fa fa-angle-left"></i></a><a class="page-number" href="/blog/">1</a><span class="page-number current">2</span><a class="page-number" href="/blog/page/3/">3</a><span class="space">&hellip;</span><a class="page-number" href="/blog/page/5/">5</a><a class="extend next" rel="next" href="/blog/page/3/"><i class="fa fa-angle-right"></i></a>
  </nav>



          </div>
          


          

        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      

      <section class="site-overview-wrap sidebar-panel sidebar-panel-active">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <img class="site-author-image" itemprop="image" src="/blog/uploads/avatar.jpg" alt="arry">
            
              <p class="site-author-name" itemprop="name">arry</p>
              <p class="site-description motion-element" itemprop="description">Java源码/设计模式/个人记录</p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/blog/archives/">
              
                  <span class="site-state-item-count">42</span>
                  <span class="site-state-item-name">posts</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/blog/categories/index.html">
                  <span class="site-state-item-count">11</span>
                  <span class="site-state-item-name">categories</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-tags">
                <a href="/blog/tags/index.html">
                  <span class="site-state-item-count">18</span>
                  <span class="site-state-item-name">tags</span>
                </a>
              </div>
            

          </nav>

          

          

          
          

          
          

          

        </div>
      </section>

      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">arry</span>

  
</div>


  <div class="powered-by">Powered by <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a></div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">Theme &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Muse</a> v5.1.4</div>




        







        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/blog/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/blog/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/blog/js/src/motion.js?v=5.1.4"></script>



  
  

  

  


  <script type="text/javascript" src="/blog/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  












  





  

  

  

  
  

  

  

  

</body>
</html>
